//-----------------------------------------------------------------------------
// Name: InputManager.cpp
// Auth: Bradford J. Smith
// Desc: maintains all information needed from frame to frame to manage the
//	input state.
//-----------------------------------------------------------------------------

#include "InputManager.h"

#include "Assert.h"

//-----------------------------------------------------------------------------
// cInputManager:
//-----------------------------------------------------------------------------
cInputManager::cInputManager() :
	mLastMouseX(0),
	mLastMouseY(0),
	mShowDebugPick(false)
{

}

cInputManager::~cInputManager()
{

}

//-----------------------------------------------------------------------------
eInputState cInputManager::GetInputState(eInputConstant inputConstant)
{
	// if the key is not registered with the map
	if (mInputStateMap.find(inputConstant) == mInputStateMap.end())
	{
		// force the entry to an initial value of up
		mInputStateMap[inputConstant] = kInputStateUp;
		return kInputStateUp;
	}
	return mInputStateMap[inputConstant];
}

void cInputManager::GetMousePosition(int& mouseX, int& mouseY)
{
	mouseX = mLastMouseX;
	mouseY = mLastMouseY;
}

bool cInputManager::GetPickCoordinates(tVector3f& origin, tVector3f& end)
{
	// retrieve the needed state variables from openGL to make the calculation
	double modelviewMatrix[16];
	double projectionMatrix[16];
	int viewport[4];
	glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix);
	glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
	glGetIntegerv(GL_VIEWPORT, viewport);

	if (mLastMouseX >= viewport[0] && mLastMouseX <= viewport[2] && 
		mLastMouseY >= viewport[1] && mLastMouseY <= viewport[3])
	{
		// set the coordinates for the near clip plane
		// invert mouse Y coordinate
		int mouseX = mLastMouseX;
		int mouseY = viewport[3] - mLastMouseY; 

		double worldX, worldY, worldZ;

		// Get unprojected end point
		gluUnProject(mouseX, mouseY, 1.0f, modelviewMatrix, projectionMatrix, viewport,
					 &worldX, &worldY, &worldZ);

		end[0] = (float)worldX;
		end[1] = (float)worldY;
		end[2] = (float)worldZ;

		// Get unprojected origin point
		gluUnProject(mouseX, mouseY, 0.0f, modelviewMatrix, projectionMatrix, viewport,
					 &worldX, &worldY, &worldZ);

		origin[0] = (float)worldX;
		origin[1] = (float)worldY;
		origin[2] = (float)worldZ;

		return true;
	}
	// not inside window space.
	return false;
}

//-----------------------------------------------------------------------------
void cInputManager::HandleMouseMove(GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;
}

//-----------------------------------------------------------------------------
void cInputManager::HandleMouseButton(GLint button, GLint state, GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;

	bool inputHandled = false;

	eInputConstant inputButton;

	// interpret the openGL button into our constants
	switch (button)
	{
	case GLUT_LEFT_BUTTON:
		inputButton = kMouseButtonLeft;
		inputHandled = true;
		break;
	case GLUT_RIGHT_BUTTON:
		inputButton = kMouseButtonRight;
		inputHandled = true;
		break;
	case GLUT_MIDDLE_BUTTON:
		inputButton = kMouseButtonMiddle;
		inputHandled = true;
		break;
	}
	
	eInputState inputState;

	// interpret the state change
	switch (state)
	{
	case GLUT_DOWN:
		inputState = kInputStateDown;
		break;
	case GLUT_UP:
		inputState = kInputStateUp;
		break;
	}

	if (inputHandled)
	{
		// assign the input to the map
		mInputStateMap[inputButton] = inputState;
	}
}

//-----------------------------------------------------------------------------
void cInputManager::HandleKeyDown(GLubyte key, GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;
}

//-----------------------------------------------------------------------------
void cInputManager::HandleKeyUp(GLubyte key, GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;
}

//-----------------------------------------------------------------------------
void cInputManager::HandleKeyDownSpecial(GLint key, GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;
}

//-----------------------------------------------------------------------------
void cInputManager::HandleKeyUpSpecial(GLint key, GLint x, GLint y)
{
	// update the mouse position
	mLastMouseX = x;
	mLastMouseY = y;
}

//-----------------------------------------------------------------------------
void cInputManager::SetDebugPickLine()
{
	// store picking info and mark the flag to display
	GetPickCoordinates(mDebugPickOrigin, mDebugPickEnd);
	mShowDebugPick = true;
}

void cInputManager::DrawDebugPickLine()
{
	if (mShowDebugPick)
	{
		// draw a debug line to respresent the picking line
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);

		glColor3f(1.0f, 0.0f, 0.0f);
		glLineWidth(1.5f);
		glBegin(GL_LINES);
			glVertex3fv(mDebugPickOrigin);
			glVertex3fv(mDebugPickEnd);
		glEnd();

		glEnable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
	}
}
